(* OS dependent. Now only runs under Linux.
 * How to handle Windows?
 *)
functor Test (S : TEST_STRUCTS) : TEST =
struct
  open S
  
  structure M = Main
  
  exception FinalSuccess
  
  datatype result
    = Suc
    | Failed
    | Not
  
  fun result2str r =
      case r
       of Suc => "[Succeeded]"
        | Failed => "[Failed]"
        | Not => "[NotRun]"
        
  datatype pass
    = T of {parse   : result,
            typeCk  : result,
            opt     : result,
            ast2ir  : result,
            srcPtr  : result,
            assem   : result,
            asmPtr  : result,
            emitPrf : result,
            asm2exe : result,
            done    : result}
  
  fun isCFile s =
      if s = "mylib.c" orelse s = "runtime.c"
      then false 
      else case (List.rev (String.explode s))
            of (#"c")::(#".")::(#"."):: _ => false
             | (#"c")::(#"."):: _ => true
             | (#"C")::(#"."):: _ => true
             | _ => false

  fun isCSFile s =
      if s = "mylib.c" orelse s = "runtime.c" orelse s = "mylib.s" orelse s = "runtime.s"
      then false 
      else case (List.rev (String.explode s))
            of (#"c")::(#".")::(#"."):: _ => false
             | (#"c")::(#"."):: _ => true
             | (#"C")::(#"."):: _ => true
             | (#"s")::(#"."):: _ => true
             | (#"S")::(#"."):: _ => true           
             | _ => false

  fun allFiles dir =
      let val all = OS.FileSys.openDir dir
          fun loop () =
              case OS.FileSys.readDir all
                of NONE => []
                 | SOME s => if isCSFile s then s::(loop()) else loop()
          val allName = loop()
      in  (OS.FileSys.closeDir all;
          allName)
      end
  
  (* dependent on the definition of the \TAB *)
  fun fileNameSpaces f =
      if String.size f>=8
      then "\t"
      else "\t\t"
      
  fun formatMain (file, T {parse  ,
                           typeCk ,
                           opt    ,
                           ast2ir ,
                           srcPtr ,
                           assem  ,
                           asmPtr ,
                           emitPrf,
                           asm2exe,
                           done   }) =
      file^"\t"^
     (result2str parse)^"\t"^
     (result2str typeCk)^"\t"^
     (result2str opt)^"\t"^
     (result2str ast2ir)^"\t"^
     (result2str srcPtr)^"\t"^
     (result2str assem)^"\t"^
     (result2str asmPtr)^"\t"^
     (result2str emitPrf)^"\t"^
     (result2str asm2exe)^"\t"^
     (result2str done)^"\t"^
     "\n"
      
  fun compileByGcc (dir, file) = "gcc "^dir^"/"^file^".s "^dir^"/mylib.c -o "^dir^"/"^file^".exe"
  
  fun run (dir, file) = "./"^dir^"/"^file^".exe >  "^dir^"/"^file^".out"
    
  fun test1 (summary, file) =
      let val _ = ()
      in  (action (File.creat (String.concat [testDir, "/", file])))
          handle M.ParseFailed => TextIO.output(summary, formatMain (file, T {parse   = Failed,
                                                                              typeCk  = Not,
                                                                              opt     = Not,
                                                                              ast2ir  = Not,
                                                                              srcPtr  = Not,
                                                                              assem   = Not,
                                                                              asmPtr  = Not,
                                                                              emitPrf = Not,
                                                                              asm2exe = Not,
                                                                              done    = Not}))
               | M.TypeCheckFailed => TextIO.output(summary, formatMain (file, T {parse   = Suc,
                                                                                  typeCk  = Failed,
                                                                                  opt     = Not,
                                                                                  ast2ir  = Not,
                                                                                  srcPtr  = Not,
                                                                                  assem   = Not,
                                                                                  asmPtr  = Not,
                                                                                  emitPrf = Not,
                                                                                  asm2exe = Not,
                                                                                  done    = Not}))
               | M.OptAstFailed => TextIO.output(summary, formatMain (file, T {parse   = Suc,
                                                                               typeCk  = Suc,
                                                                               opt     = Failed,
                                                                               ast2ir  = Not,
                                                                               srcPtr  = Not,
                                                                               assem   = Not,
                                                                               asmPtr  = Not,
                                                                               emitPrf = Not,
                                                                               asm2exe = Not,
                                                                               done    = Not}))
               | M.Ast2IrFailed => TextIO.output(summary, formatMain (file, T {parse   = Suc,
                                                                               typeCk  = Suc,
                                                                               opt     = Suc,
                                                                               ast2ir  = Failed,
                                                                               srcPtr  = Not,
                                                                               assem   = Not,
                                                                               asmPtr  = Not,
                                                                               emitPrf = Not,
                                                                               asm2exe = Not,
                                                                               done    = Not}))
               | M.SrcPtrLogicFailed => TextIO.output (summary, formatMain (file, T {parse   = Suc,
                                                                                     typeCk  = Suc,
                                                                                     opt     = Suc,
                                                                                     ast2ir  = Suc,
                                                                                     srcPtr  = Failed,
                                                                                     assem   = Not,
                                                                                     asmPtr  = Not,
                                                                                     emitPrf = Not,
                                                                                     asm2exe = Not,
                                                                                     done    = Not}))
               | M.EmitAssemFailed => TextIO.output(summary, formatMain (file, T {parse   = Suc,
                                                                                  typeCk  = Suc,
                                                                                  opt     = Suc,
                                                                                  ast2ir  = Suc,
                                                                                  srcPtr  = Suc,
                                                                                  assem   = Failed,
                                                                                  asmPtr  = Not,
                                                                                  emitPrf = Not,
                                                                                  asm2exe = Not,
                                                                                  done    = Not}))
               | M.AsmPtrLogicFailed => TextIO.output(summary, formatMain (file, T {parse   = Suc,
                                                                                    typeCk  = Suc,
                                                                                    opt     = Suc,
                                                                                    ast2ir  = Suc,
                                                                                    srcPtr  = Suc,
                                                                                    assem   = Suc,
                                                                                    asmPtr  = Failed,
                                                                                    emitPrf = Not,
                                                                                    asm2exe = Not,
                                                                                    done    = Not}))
               | M.EmitProofFailed => TextIO.output(summary, formatMain (file, T {parse   = Suc,
                                                                                  typeCk  = Suc,
                                                                                  opt     = Suc,
                                                                                  ast2ir  = Suc,
                                                                                  srcPtr  = Suc,
                                                                                  assem   = Suc,
                                                                                  asmPtr  = Suc,
                                                                                  emitPrf = Failed,
                                                                                  asm2exe = Not,
                                                                                  done    = Not}))
               | M.ExeFailed => TextIO.output(summary, formatMain (file, T {parse   = Suc,
                                                                            typeCk  = Suc,
                                                                            opt     = Suc,
                                                                            ast2ir  = Suc,
                                                                            srcPtr  = Suc,
                                                                            assem   = Suc,
                                                                            asmPtr  = Suc,
                                                                            emitPrf = Suc,
                                                                            asm2exe = Failed,
                                                                            done    = Not}))
               | M.Success => TextIO.output(summary, formatMain (file, T {parse   = Suc,
                                                                          typeCk  = Suc,
                                                                          opt     = Suc,
                                                                          ast2ir  = Suc,
                                                                          srcPtr  = Suc,
                                                                          assem   = Suc,
                                                                          asmPtr  = Suc,
                                                                          emitPrf = Suc,
                                                                          asm2exe = Suc,
                                                                          done    = Suc}))
      end
      
  fun creatDir dir =
      (if OS.FileSys.isDir dir
      then ()
      else OS.FileSys.mkDir dir) handle SysErr => OS.FileSys.mkDir dir
      
  fun test () =
      let val _ = creatDir outDir
          val summary = TextIO.openOut (outDir^"/summary-for-main")
          val _ = TextIO.output(summary, "File:\tLexing&Parsing\tTypeChecking\tOptimization\
                                         \ \tAst2Inter\tSrcPtrLogic\tToAssembly\tAsmPtrLogic\tEmitProof\tToBinary\tRun\n")
          val _ = TextIO.output(summary, "====================================================================\
                                         \===========================================================\
                                         \==============================================\n")
          fun loop [] = TextIO.closeOut summary
            | loop (file::files) = (test1 (summary, file); loop files)
      in  loop (allFiles testDir)
      end      
end
